/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/src/lib/unencode.c,v 1.3 2003/04/24 22:59:23 bboy Exp $
	unencode.c: Routine to unencode a DNS-encoded name.

	Copyright (C) 2002-2003  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "mydns.h"


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	NAME_UNENCODE
	Get a name from a compressed question.
	Returns the new location of src, or NULL if an error occurred.
	If an error occurs, puts a descriptive message in `dest'.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
name_unencode(char *start, size_t slen, char *current, char *dest, size_t destsize)
{
	register char	*s = current,								/* Current pointer into input */
						*d = dest,									/* Current pointer into destination */
						n, len, *rp = NULL;

#define CHECK_DEST_SPACE(n)  \
	if (d >= dest+(destsize-(n))) \
	{ \
		dest[0] = ERR_Q_BUFFER_OVERFLOW; \
		return (NULL); \
	} \

	if (*s == 0)													/* The name is just "." */
	{
		CHECK_DEST_SPACE(2);
		*d++ = '.';
		*d = '\0';
		return (s+1);
	}

	while ((len = *s))											/* Get length octet */
	{
		register unsigned char mask = len & 0xC0;
		if (mask == 0xC0)
		{
			uint16_t offset;

			DNS_GET16(offset, s);
			offset &= ~0xC000;
			if (!rp)
				rp = s;
			s = start + offset;
		}
		else if (mask == 0x40 || mask == 0x80)
		{
			dest[0] = ERR_Q_INVALID_COMPRESSION;
			return (NULL);
		}
		else
		{
			s++;
			if (len + (s - current) > DNS_MAXNAMELEN)
			{
				dest[0] = ERR_Q_NAME_TOO_LONG;
				return (NULL);
			}
			for (n = 0; n < len; n++)							/* Get label */
			{
				CHECK_DEST_SPACE(1);
				*d++ = tolower(*s++);
			}
			CHECK_DEST_SPACE(1);
			*d++ = '.';
		}
	}
	CHECK_DEST_SPACE(1);
	*d = '\0';
	return (rp ? rp : s+1);
}
/*--- name_unencode() ---------------------------------------------------------------------------*/

/* vi:set ts=3: */
